use std::net::SocketAddr;

use axum::{Router, extract::Request, http::request};
use tower_http::trace::{DefaultOnResponse, TraceLayer};

use crate::{app::AppState, config::ServerConfig, latency::LatencyOnReponse};

pub struct Server {
  config: &'static ServerConfig,
}

impl Server {
  pub fn new(config: &'static ServerConfig) -> Self {
    Self { config }
  }

  pub async fn start(
    &self,
    state: AppState,
    router: Router<AppState>,
  ) -> anyhow::Result<()> {
    let router = self.build_router(state, router);
    let port = self.config.port();
    let addr = format!("0.0.0.0:{port}");
    let listener = tokio::net::TcpListener::bind(&addr).await?;
    tracing::info!("Listening on http://{}", listener.local_addr()?);
    axum::serve(
      listener,
      router.into_make_service_with_connect_info::<SocketAddr>(),
    )
    .await?;
    Ok(())
  }

  fn build_router(&self, state: AppState, router: Router<AppState>) -> Router {
    let tracing =
      TraceLayer::new_for_http().make_span_with(|request: &Request| {
        let method = request.method();
        let path = request.uri().path();
        let id = xid::new();
        // % 可以为实现了Display的自动调用 to_string()
        tracing::info_span!("api request",id = %id, method = %method, path = %path)
      })
        .on_request(())
        .on_failure(())
        .on_response(LatencyOnReponse); // 自定义
    // .on_response(
    //     DefaultOnResponse::new().level(tracing::Level::INFO)
    // );
    // 从后往前执行, 洋葱模式
    Router::new().merge(router).layer(tracing).with_state(state)
  }
}
